﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

using System.Collections.Generic;
using System.IO;
using System.Web;
using ScrumTable.Common.Collections;

namespace ScrumTable.Common
{
    #region Usings

    using System;
    using ScrumTable.Common.Properties;

    #endregion

    /// <summary>
    /// Contains the global utilities for the URI class.
    /// </summary>
    public static class UriUtil
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets an empty uri instance.
        /// </summary>
        public static readonly Uri Empty = new Uri(string.Empty, UriKind.Relative);

        /// <summary>
        /// Gets the schema for file Uris.
        /// </summary>
        public const string FileSchema = "file";

        private const string QueryDelimiter = "?";
        private const string QuerySeparator = "&";
        private const string QueryValueSeparator = "=";
        private const string UriSeparator = "/";

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the query part of the specified uri and cuts off the delimiter '?'
        /// character.
        /// </summary>
        /// <param name="pathRoot">Specifies the path root as a string.</param>
        /// <param name="elements">Specifies the elements to join to the query.</param>
        /// <returns>Returns the evaluated query or an empty string.</returns>
        public static string CreateQueryString(string pathRoot, IEnumerable<Pair<string, string>> elements)
        {
            PreCondition.AssertNotNull(elements, "elements");

            string data = pathRoot ?? string.Empty;

            foreach (var rp in elements)
            {
                if (data.Length > 0 && data.Substring(data.Length - 1, 1) != QueryDelimiter)
                {
                    data += QuerySeparator;
                }
                data += rp.Left + QueryValueSeparator + HttpUtility.UrlEncode(rp.Right);
            }
            return data;
        }

        /// <summary>
        /// Gets the query part of the specified uri and cuts off the delimiter '?'
        /// character.
        /// </summary>
        /// <param name="baseUri">Specifies the base uri instance.</param>
        /// <returns>Returns the evaluated query or an empty string.</returns>
        public static string GetQueryWithoutDelimiter(this Uri baseUri)
        {
            PreCondition.AssertNotNull(baseUri, "baseUri");

            string uri = baseUri.ToString();

            if (uri.Contains(QueryDelimiter))
            {
                return uri.Substring(uri.IndexOf(QueryDelimiter) + 1);
            }
            return string.Empty;
        }

        /// <summary>
        /// Returns true if the given uri contains a relative or absolute file
        /// path.
        /// </summary>
        /// <param name="baseUri">Specifies the uri to check.</param>
        public static bool ContainsFilePath(this Uri baseUri)
        {
            PreCondition.AssertNotNull(baseUri, "baseUri");

            return (baseUri.IsAbsoluteUri) ? (baseUri.Scheme == FileSchema) : true;
        }

        /// <summary>
        /// Combines the current uri with the given url (absolute or relative
        /// to the baseUri path) and returns the combined uri.
        /// </summary>
        /// <param name="baseUri">Base uri, this ptr.</param>
        /// <param name="urlToCombine">Specifies the url to combine.</param>
        /// <returns>Returns the combined uri.</returns>
        public static Uri ReplacePath(this Uri baseUri, string urlToCombine)
        {
            PreCondition.AssertNotNull(baseUri, "baseUri");
            PreCondition.AssertNotNullOrEmpty(urlToCombine, "urlToCombine");

            Uri newUri = null;

            if (urlToCombine.StartsWith(UriSeparator))
            {
                if (!Uri.TryCreate(baseUri, urlToCombine, out newUri))
                    throw new NotSupportedException(Resources.ExcUriUtilInvalidUrlToCombine);
            }
            else
            {
                string baseUriString = baseUri.GetLeftPart(UriPartial.Path);

                if (!baseUriString.EndsWith(UriSeparator))
                    baseUriString += UriSeparator;

                Uri newBaseUri = new Uri(baseUriString + baseUri.Query + baseUri.Fragment);

                if (!Uri.TryCreate(newBaseUri, urlToCombine, out newUri))
                    throw new NotSupportedException(Resources.ExcUriUtilInvalidUrlToCombine);
            }
            return newUri;
            
        }

        /// <summary>
        /// Combines the current uri with the given query string and returns the
        /// combined uri.
        /// </summary>
        /// <param name="baseUri">Base uri, this ptr.</param>
        /// <param name="qStrToCombine">Specifies the query string to combine.</param>
        /// <returns>Returns the combined uri.</returns>
        public static Uri CombineQuery(this Uri baseUri, string qStrToCombine)
        {
            PreCondition.AssertNotNull(baseUri, "baseUri");
            PreCondition.AssertNotNull(qStrToCombine, "qStrToCombine");

            string queryString = (!qStrToCombine.StartsWith(QueryDelimiter))
                                     ? HttpUtility.UrlEncode(qStrToCombine)
                                     : qStrToCombine.Substring(1, QueryDelimiter.Length - 1);

            if (string.IsNullOrEmpty(baseUri.Query))
                return new Uri(baseUri + QueryDelimiter + queryString);
            return new Uri(baseUri + QuerySeparator + queryString);
        }

        /// <summary>
        /// Combines the current uri with the given query string and returns the
        /// combined uri.
        /// </summary>
        /// <param name="baseUri">Base uri, this ptr.</param>
        /// <param name="qStrToCombine">Specifies the query string to combine.</param>
        /// <returns>Returns the combined uri.</returns>
        public static Uri CombineQueryAsPath(this Uri baseUri, string qStrToCombine)
        {
            PreCondition.AssertNotNull(baseUri, "baseUri");
            PreCondition.AssertNotNull(qStrToCombine, "qStrToCombine");

            string queryString = (!qStrToCombine.StartsWith(QueryDelimiter))
                                     ? HttpUtility.UrlPathEncode(qStrToCombine)
                                     : qStrToCombine.Substring(1, QueryDelimiter.Length - 1);

            if (string.IsNullOrEmpty(baseUri.Query))
                return new Uri(baseUri + QueryDelimiter + queryString);
            return new Uri(baseUri + QuerySeparator + queryString);
        }

        /// <summary>
        /// Replaces the query information of the given baseUri with the qStrToCombine.
        /// </summary>
        /// <param name="baseUri">Base uri, this ptr.</param>
        /// <param name="qStrToCombine">Specifies the query string to insert.</param>
        /// <returns>Returns the new uri.</returns>
        public static Uri ReplaceQuery(this Uri baseUri, string qStrToCombine)
        {
            PreCondition.AssertNotNull(baseUri, "baseUri");

            return new Uri(
                baseUri.GetLeftPart(UriPartial.Path) + QueryDelimiter + qStrToCombine,
                UriKind.RelativeOrAbsolute);
        }

        /// <summary>
        /// Gets a local operating-system representation of a file name.
        /// </summary>
        /// <param name="baseUri">Base uri, this ptr.</param>
        /// <remarks>
        /// This method will not throw an exception if the uri is relative.
        /// </remarks>
        public static string ToLocalPath(this Uri baseUri)
        {
            if (baseUri == null)
                return null;

            if (!baseUri.IsAbsoluteUri)
                return baseUri.ToString().Replace('/', Path.DirectorySeparatorChar);
            return baseUri.LocalPath;
        }


        /// <summary>
        /// Gets true if the uri is a file
        /// </summary>
        /// <param name="baseUri">Base uri, this ptr.</param>
        public static bool TryGetIsFile(this Uri baseUri)
        {
            if (baseUri == null)
                return false;

            if (!baseUri.IsAbsoluteUri)
                return File.Exists(baseUri.ToLocalPath());
            return baseUri.IsFile;
        } 

        #endregion
    }
}
